package api

import (
	"api_gateway/basic/config"
	chat "api_gateway/basic/proto/chat"
	"api_gateway/untils"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/streadway/amqp"
	"log"
	"time"
)

func PushPrivateOfflineToUser() {
	// 初始化rabbitmq连接
	rabbitmq := untils.NewRabbitMQSimple(config.PRIVATE_OFFLINE)

	// 从队列中获取消息
	private, err := rabbitmq.ConsumeSimple()
	if err != nil {
		log.Printf("获取队列消息失败: %v", err)
		return
	}
	log.Printf(" [*] 离线消息推送服务启动，等待消息...")

	// 处理消息
	forever := make(chan bool)

	// 启用协程处理消息
	go func() {
		for d := range private {
			// 异步处理每条消息
			go storeOfflineMessage(d)
		}
	}()

	<-forever
}

// 存储离线消息到Redis
func storeOfflineMessage(d amqp.Delivery) {
	log.Printf("存储离线消息:%s", string(d.Body))

	var messageData map[string]interface{}
	if err := json.Unmarshal(d.Body, &messageData); err != nil {
		log.Printf("解析消息失败: %v", err)
		d.Ack(false)
		return
	}

	// 提取目标用户ID
	toTargetId, ok := messageData["toTargetId"].(float64)
	if !ok {
		log.Printf("消息中缺少目标用户ID: %v", messageData)
		d.Ack(false)
		return
	}

	// 保存到Redis
	if err := saveOfflineMessageToRedis(messageData); err != nil {
		log.Printf("保存Redis失败: %v", err)
		d.Nack(false, true) // 保存失败重新入队
		return
	}

	// 保存到服务端
	saveMessageToServer(messageData)
	d.Ack(false)
	log.Printf("离线消息存储完成，用户ID: %d", uint(toTargetId))
}

// 保存离线消息到Redis
func saveOfflineMessageToRedis(messageData map[string]interface{}) error {
	// 构建Redis存储的数据结构
	redisMessage := map[string]interface{}{
		"message_id":   fmt.Sprintf("private_%d", time.Now().UnixNano()),
		"formUserId":   messageData["formUserId"],
		"toTargetId":   messageData["toTargetId"],
		"content":      messageData["content"],
		"message_type": messageData["message_type"],
		"timestamp":    time.Now().Format(time.DateTime),
		"status":       "offline",
	}

	// 序列化为JSON
	messageJSON, err := json.Marshal(redisMessage)
	if err != nil {
		return err
	}

	// 使用Redis列表存储用户的离线消息
	toTargetId := messageData["toTargetId"].(float64)
	redisKey := fmt.Sprintf("offline_messages:%d", int64(toTargetId))

	// 将消息添加到用户离线消息列表
	err = config.RedisClient.RPush(config.Ctx, redisKey, messageJSON).Err()
	if err != nil {
		return err
	}

	// 设置过期时间（7天）
	config.RedisClient.Expire(config.Ctx, redisKey, 7*24*time.Hour)
	return nil
}

// 保存消息到服务端
func saveMessageToServer(messageData map[string]interface{}) {
	var privateMessage chat.SendMessageReq
	if err := mapToMessageReq(messageData, &privateMessage); err != nil {
		log.Printf("转换消息结构失败: %v", err)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 调用服务端保存消息
	response, err := config.ChatClient.SendMessage(ctx, &privateMessage)
	if err != nil {
		log.Printf("调用通讯服务失败: %v", err)
		return
	}

	log.Printf("消息保存成功: 消息ID=%s", response.MessageId)
}

// Map转换为SendMessageReq
func mapToMessageReq(data map[string]interface{}, req *chat.SendMessageReq) error {
	if formId, ok := data["formUserId"].(float64); ok {
		req.FormUserId = int64(formId)
	}
	if toId, ok := data["toTargetId"].(float64); ok {
		req.ToTargetId = int64(toId)
	}
	if content, ok := data["content"].(string); ok {
		req.Content = content
	}
	if msgType, ok := data["messageType"].(float64); ok {
		req.MessageType = int64(msgType)
	}
	return nil
}

// 推送消息给用户
func pushMessageToUser(conn *websocket.Conn, msgData map[string]interface{}) bool {
	// 构建消息
	message := map[string]interface{}{
		"type":        "privateMessage",
		"formUserId":  msgData["formUserId"],
		"content":     msgData["content"],
		"messageType": msgData["messageType"],
		"timestamp":   time.Now().Unix(),
	}

	response := WSResp{
		Code: 200,
		Msg:  "成功",
		Data: message,
	}

	responseStr, err := json.Marshal(response)
	if err != nil {
		log.Printf("序列化消息失败: %v", err)
		return false
	}

	// 设置写超时
	conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
	if err := conn.WriteMessage(websocket.TextMessage, responseStr); err != nil {
		log.Printf("推送消息失败: %v", err)
		return false
	}

	return true
}

// 用户上线时从Redis推送离线消息
func PushOfflineMessagesOnUserLogin(userId uint, conn *websocket.Conn) {
	log.Printf("用户 %d 上线，从Redis获取离线消息", userId)

	messages, err := getOfflineMessagesFromRedis(int64(userId))
	if err != nil {
		log.Printf("从Redis获取离线消息失败: %v", err)
		return
	}

	if len(messages) == 0 {
		log.Printf("用户 %d 没有离线消息", userId)
		return
	}

	successCount := 0
	for i, msg := range messages {
		if pushMessageToUser(conn, msg) {
			successCount++
			log.Printf("用户 %d 离线消息推送成功 [%d/%d]: %s",
				userId, i+1, len(messages), msg["content"])
		}
		// 控制推送速度
		time.Sleep(50 * time.Millisecond)
	}

	log.Printf("用户 %d 离线消息推送完成: 成功 %d/%d 条", userId, successCount, len(messages))

	// 推送成功后清理Redis中的离线消息
	if successCount > 0 {
		go cleanupRedisOfflineMessages(userId, successCount)
	}
}

// 从Redis获取用户的离线消息
func getOfflineMessagesFromRedis(userId int64) ([]map[string]interface{}, error) {
	redisKey := fmt.Sprintf("offline_messages:%d", userId)

	// 获取列表长度
	listLen, err := config.RedisClient.LLen(config.Ctx, redisKey).Result()
	if err != nil {
		return nil, err
	}

	if listLen == 0 {
		return []map[string]interface{}{}, nil
	}

	// 获取所有离线消息
	messages, err := config.RedisClient.LRange(config.Ctx, redisKey, 0, -1).Result()
	if err != nil {
		return nil, err
	}

	var result []map[string]interface{}
	for _, msg := range messages {
		var messageData map[string]interface{}
		if err := json.Unmarshal([]byte(msg), &messageData); err == nil {
			result = append(result, messageData)
		}
	}

	log.Printf("从Redis获取用户 %d 的 %d 条离线消息", userId, len(result))
	return result, nil
}

// 清理Redis中的离线消息
func cleanupRedisOfflineMessages(userId uint, count int) {
	redisKey := fmt.Sprintf("offline_messages:%d", userId)

	// 删除已推送的消息（从左侧开始删除）
	err := config.RedisClient.LTrim(config.Ctx, redisKey, int64(count), -1).Err()
	if err != nil {
		log.Printf("清理Redis离线消息失败: %v", err)
		return
	}

	// 检查剩余消息数量
	remaining, _ := config.RedisClient.LLen(config.Ctx, redisKey).Result()
	log.Printf("已清理用户 %d 的 %d 条离线消息，剩余 %d 条", userId, count, remaining)

	// 如果没有消息了，删除key
	if remaining == 0 {
		config.RedisClient.Del(config.Ctx, redisKey)
	}
}
